﻿var ____a;

var _PseudoClassType =
{
	FirstChild: 0,
	LastChild: 1,
	OnlyChild: 2,
	NthChild: 5,
	NthLastChild: 6,
	FirstOfType: 10,
	LastOfType: 11,
	OnlyOfType: 12,
	NthOfType: 15,
	NthLastOfType: 16,
	Empty: 20,
	Enabled: 30,
	Disabled: 31,
	Checked: 40
};
var _PseudoClassState =
{
	Name: 0,
	ExpectIndex: 10,
	NumberA: 20,
	ExpectOperator: 30,
	ExpectNumberB: 40,
	NumberB: 50,
	ExpectEnd: 60
};


function _PseudoClassComparison()
{
	this._a = this._b = 0;
}
if (____a)
{
	_PseudoClassComparison.prototype._type = _PseudoClassType.FirstChild;
	_PseudoClassComparison.prototype._a = 0;
	_PseudoClassComparison.prototype._b = 0;
}

_PseudoClassComparison._IsIndexedType = function _IsIndexedType(type)
{
	switch (type)
	{
		case _PseudoClassType.NthChild:
		case _PseudoClassType.NthLastChild:
		case _PseudoClassType.NthOfType:
		case _PseudoClassType.NthLastOfType:
			return true;
	}
	return false;
}
_PseudoClassComparison._GetTypeByName = function _GetTypeByName(name)
{
	switch (name.toLowerCase())
	{
		case "first-child":
			return _PseudoClassType.FirstChild;
		case "last-child":
			return _PseudoClassType.LastChild;
		case "only-child":
			return _PseudoClassType.OnlyChild;
		case "nth-child":
			return _PseudoClassType.NthChild;
		case "nth-last-child":
			return _PseudoClassType.NthLastChild;

		case "first-of-type":
			return _PseudoClassType.FirstOfType;
		case "last-of-type":
			return _PseudoClassType.LastOfType;
		case "only-of-type":
			return _PseudoClassType.OnlyOfType;
		case "nth-of-type":
			return _PseudoClassType.NthOfType;
		case "nth-last-of-type":
			return _PseudoClassType.NthLastOfType;

		case "enabled":
			return _PseudoClassType.Enabled;
		case "disabled":
			return _PseudoClassType.Disabled;

			//		case "selection":
			//			return _PseudoClassType.Selection;
		case "checked":
			return _PseudoClassType.Checked;

		case "empty":
			return _PseudoClassType.Empty;
	}
	FindPath.ThrowError("unknown pseudo-class name '" + name + "'");
}
_PseudoClassComparison._IsMatch = function _IsMatch(c, state)
{
	switch (state)
	{
		case _PseudoClassState.Name:
			return Char.IsAlphaNumeric(c) || c == '-';
		case _PseudoClassState.NumberA:
		case _PseudoClassState.NumberB:
			return Char.IsNumeric(c) || c == '-';
	}
	return false;
}
_PseudoClassComparison.prototype._Parse = function _Parse(path)
{
	var state = _PseudoClassState.Name;
	var sb = new StringBuilder();
	for (; path._index <= path._path.length; ++path._index)
	{
		var c = path._GetChar();

		if (_PseudoClassComparison._IsMatch(c, state))
		{
			sb.Append(c);
		}
		else
		{
			var identifier = sb.toString();
			sb.Clear();

			switch (state)
			{
				case _PseudoClassState.Name:
					if (identifier == "")
						FindPath.ThrowError("pseudo class name can't be empty");

					this._type = _PseudoClassComparison._GetTypeByName(identifier);

					if (_PseudoClassComparison._IsIndexedType(this._type))
					{
						if (c != '(')
							FindPath.ThrowError("expected '(' after an indexed pseudo class");

						state = _PseudoClassState.ExpectIndex;
					}
					else
					{
						path._GoBack();
						return;
					}
					break;
				case _PseudoClassState.ExpectIndex:
					if (c == 'o')
					{
						path._VerifyNextChar('d');
						path._VerifyNextChar('d');

						this._a = 2; this._b = -1;

						state = _PseudoClassState.ExpectEnd;
					}
					else if (c == 'e')
					{
						path._VerifyNextChar('v');
						path._VerifyNextChar('e');
						path._VerifyNextChar('n');

						this._a = 2; this._b = 0;

						state = _PseudoClassState.ExpectEnd;
					}
					else if (_PseudoClassComparison._IsMatch(c, _PseudoClassState.NumberA))
					{
						path._GoBack();
						state = _PseudoClassState.NumberA;
					}
					else if (Char.IsWhiteSpace(c))
					{
					}
					else
						FindPath.InvalidCharacterError(c);
					break;
				case _PseudoClassState.NumberA:
					if (identifier == "")
						FindPath.ThrowError("number can't be an empty string");

					if (c == 'n')
					{
						if (identifier == "-")
						{
							this._a = -1;
						}
						else
						{
							this._a = parseInt(identifier);
							if (isNaN(this._a))
								FindPath.ThrowError("'a' number not a number");
						}

						state = _PseudoClassState.ExpectOperator;
					}
					else
					{
						
						this._b = parseInt(identifier);
						if (isNaN(this._b))
							FindPath.ThrowError("'b' number not a number");

						state = _PseudoClassState.ExpectEnd;
						path._GoBack();
					}
					break;
				case _PseudoClassState.ExpectOperator:
					if (c == '+')
					{
						state = _PseudoClassState.NumberB;
					}
					else if (c == '-')
					{
						path._GoBack();
						state = _PseudoClassState.NumberB;
					}
					else if (c == ')') // end
					{
						state = _PseudoClassState.ExpectEnd;
						path._GoBack();

					}
					else if (Char.IsWhiteSpace(c))
					{
					}
					else
						FindPath.InvalidCharacterError(c);
					break;
				case _PseudoClassState.NumberB:
					this._b = parseInt(identifier);
					if (isNaN(this._b))
						FindPath.ThrowError("'b' number not a number");

					state = _PseudoClassState.ExpectEnd;
					path._GoBack();
					break;
				case _PseudoClassState.ExpectEnd:
					if (c == ')')
					{
						return;
					}
					else if (Char.IsWhiteSpace(c))
					{
					}
					else
						FindPath.InvalidCharacterError(c);
					break;
			}
		}
	}
}

_PseudoClassComparison.prototype._ResolveType = function _ResolveType()
{
	switch (this._type)
	{
		case _PseudoClassType.FirstChild:
			this._type = _PseudoClassType.NthChild;
			this._b = 1;
			break;
		case _PseudoClassType.LastChild:
			this._type = _PseudoClassType.NthLastChild;
			this._b = 1;
			break;
		case _PseudoClassType.FirstOfType:
			this._type = _PseudoClassType.NthOfType;
			this._b = 1;
			break;
		case _PseudoClassType.LastOfType:
			this._type = _PseudoClassType.NthLastOfType;
			this._b = 1;
			break;
	}
}
_PseudoClassComparison._GetElementIndex = function _GetElementIndex(elem, direction, ofType)
{
	if (elem.parentNode == null)
		Throw("Element's parentNode is null. Can't get its index.");

	var cn = elem.parentNode.get_ChildNodes(), cnl = cn.length, index = 1;
	for (var i = 0; i < cnl; ++i)
	{
		if (cn[i] == elem)
		{
			if (direction > 0)
				return index;
			else
				return cnl - index + 1;
		}
		if (!ofType || elem.tagName == cn[i].tagName)
			++index;
	}
	Throw("Element not found among its parent's children (weird).");
}
_PseudoClassComparison.prototype._CheckIndex = function _CheckIndex(index)
{
	if (this._a == 0)
		return this._b == index;
	else
		return Math.abs(index - this._b) % Math.abs(this._a) == 0;
}
_PseudoClassComparison.prototype._Check = function _Check(elem)
{
	this._ResolveType();

	switch (this._type)
	{
		case _PseudoClassType.NthChild:
			var index = _PseudoClassComparison._GetElementIndex(elem, 1, false);
			return this._CheckIndex(index);
		case _PseudoClassType.NthLastChild:
			var index = _PseudoClassComparison._GetElementIndex(elem, -1, false);
			return this._CheckIndex(index);
		case _PseudoClassType.NthOfType:
			var index = _PseudoClassComparison._GetElementIndex(elem, 1, true);
			return this._CheckIndex(index);
		case _PseudoClassType.NthLastOfType:
			var index = _PseudoClassComparison._GetElementIndex(elem, -1, true);
			return this._CheckIndex(index);

		case _PseudoClassType.OnlyChild:
			return elem.parentNode.childNodes.length == 1 && elem.parentNode.childNodes[0] == elem;
		case _PseudoClassType.OnlyOfType:
			var cn = elem.parentNode.get_ChildNodes(), cnl = cn.length;
			for (var i = 0; i < cnl; ++i)
			{
				if (cn[i].tagName == elem.tagName && cn[i] != elem)
					return false;
			}
			return true;

		case _PseudoClassType.Empty:
			return elem.childNodes.length == 0;
		case _PseudoClassType.Checked:
			return elem.tagName == "INPUT" && elem.checked === true;
		case _PseudoClassType.Enabled:
			return elem.disabled !== true;
		case _PseudoClassType.Disabled:
			return elem.disabled === true;
			//		case _PseudoClassType.Selection:
			//			Throw("Not implemented (Selection).");
	}
}




































var _PathAttributeOperator =
{
	Exists: -1,
	Equals: 0,
	ContainsSpaced: 1,
	StartsWith: 2,
	EndsWith: 3,
	Contains: 4
};
var _PathAttributeState =
{
	ExpectName: 0,
	Name: 1,
	QuotedName: 2,
	ExpectOperator: 3,
	ExpectValue: 4,
	Value: 5,
	QuotedValue: 6,
	ExpectEnd: 7
};











function _AttributeComparison()
{
}
if (____a)
{
	_AttributeComparison.prototype._operator = _PathAttributeOperator.Equals;
	_AttributeComparison.prototype._name = "";
	_AttributeComparison.prototype._value = "";
}


_AttributeComparison._IsMatch = function _IsMatch(c, state)
{
	switch (state)
	{
		case _PathAttributeState.Name:
		case _PathAttributeState.Value:
			return Char.IsAlphaNumeric(c) || c == '_' || c == '-';
		case _PathAttributeState.QuotedName:
		case _PathAttributeState.QuotedValue:
			return c != '"';
	}
	return false;
}
_AttributeComparison.prototype._Parse = function _Parse(path)
{
	var state = _PathAttributeState.ExpectName;
	var sb = new StringBuilder();
	for (; path._index <= path._path.length; ++path._index)
	{
		var c = path._GetChar();

		if (_AttributeComparison._IsMatch(c, state))
		{
			sb.Append(c);
		}
		else
		{
			var identifier = sb.toString();
			sb.Clear();

			switch (state)
			{
				case _PathAttributeState.ExpectName:
					if (c == '"')
						state = _PathAttributeState.QuotedName;
					else if (_AttributeComparison._IsMatch(c, _PathAttributeState.Name))
					{
						path._GoBack();
						state = _PathAttributeState.Name;
					}
					else
						FindPath.ThrowError("expected '\"' or identifier, found '" + c + "'");
					break;
				case _PathAttributeState.Name:
				case _PathAttributeState.QuotedName:
					this._name = identifier;

					if (state == _PathAttributeState.Name)
						path._GoBack();
					state = _PathAttributeState.ExpectOperator;
					break;
				case _PathAttributeState.ExpectOperator:
					switch (c)
					{
						case ']':
							this._operator = _PathAttributeOperator.Exists;
							return;
						case '=':
							this._operator = _PathAttributeOperator.Equals;
							state = _PathAttributeState.ExpectValue;
							break;
						case '~':
							c = path._GetNextChar();
							if (c != '=')
								FindPath.ThrowError("expected '=' after '~' ('name~=value'), found '" + c + "'");
							this._operator = _PathAttributeOperator.ContainsSpaced;
							state = _PathAttributeState.ExpectValue;
							break;
						case '^':
							c = path._GetNextChar();
							if (c != '=')
								FindPath.ThrowError("expected '=' after '^' ('name^=value'), found '" + c + "'");
							this._operator = _PathAttributeOperator.StartsWith;
							state = _PathAttributeState.ExpectValue;
							break;
						case '$':
							c = path._GetNextChar();
							if (c != '=')
								FindPath.ThrowError("expected '=' after '$' ('name$=value'), found '" + c + "'");
							this._operator = _PathAttributeOperator.EndsWith;
							state = _PathAttributeState.ExpectValue;
							break;
						case '*':
							c = path._GetNextChar();
							if (c != '=')
								FindPath.ThrowError("expected '=' after '*' ('name*=value'), found '" + c + "'");
							this._operator = _PathAttributeOperator.Contains;
							state = _PathAttributeState.ExpectValue;
							break;
						default:
							if (!Char.IsWhiteSpace(c))
								FindPath.ThrowError("attribute operator expected ('=', '~=', '^=', '$=', '*=') but found '" + c + "'");
							break;
					}
					break;
				case _PathAttributeState.ExpectValue:
					if (c == '"')
					{
						state = _PathAttributeState.QuoteValue;
					}
					else if (_AttributeComparison._IsMatch(c, _PathAttributeState.Value))
					{
						state = _PathAttributeState.Value;
						path._GoBack();
					}
					else if (!Char.IsWhiteSpace(c))
						FindPath.ThrowError("expected '\"' or identifier, found '" + c + "'");
					break;
				case _PathAttributeState.Value:
				case _PathAttributeState.QuotedValue:
					this._value = identifier;
					if (state == _PathAttributeState.Value)
						path._GoBack();
					state = _PathAttributeState.ExpectEnd;
					break;
				case _PathAttributeState.ExpectEnd:
					if (c == ']')
						return;
					if (!Char.IsWhiteSpace(c))
						FindPath.ThrowError("expected ']', found '" + c + "'");
					break;
			}
		}
	}
}

_AttributeComparison.prototype._Check = function _Check(elem)
{
	var v = elem.getAttribute(this._name);
	if (v == null) return false;
	if (____a)
		v = "";
	switch (this._operator)
	{
		case _PathAttributeOperator.Exists:
			return true; // existence already established
		case _PathAttributeOperator.Equals:
			return v == this._value;
		case _PathAttributeOperator.Contains:
			return v.Contains(this._value);
		case _PathAttributeOperator.ContainsSpaced:
			return v == this._value ||
				v.indexOf(this._value + " ") != -1 ||
				v.indexOf(" " + this._value) != -1;
		case _PathAttributeOperator.StartsWith:
			return v.indexOf(this._value) == 0;
		case _PathAttributeOperator.EndsWith:
			return v.substr(v.length - this._value.length) == this._value;
	}
	FindPath.ThrowError("Unknown attribute operator " + this._operator);
}


































var _PathParserState = {
	Expect: 0,
	TagName: 1,
	ClassName: 10,
	Ids: 30,
	PseudoClass: 40
};
var _PathElementJoin = {
	Descendant: 0,
	Child: 1,
	ImmediatePreviousSibling: 2,
	PreviousSibling: 3
};







function _FindPathStep()
{
	this._tagNames = new Array();
	this._classNames = new Array();
	this._ids = new Array();
	this._attributes = new Array();
	this._pseudoClasses = new Array();
}
if (____a)
{
	_FindPathStep.prototype._tagNames = new Array();
	_FindPathStep.prototype._classNames = new Array();
	_FindPathStep.prototype._ids = new Array();
	_FindPathStep.prototype._attributes = new Array();
	_FindPathStep.prototype._pseudoClasses = new Array();
	_FindPathStep.prototype._join = _PathElementJoin.Descendant;
}


_FindPathStep._IsMatch = function _IsMatch(c, state)
{
	switch (state)
	{
		case _PathParserState.ClassName:
		case _PathParserState.Ids:
			return Char.IsAlphaNumeric(c) || c == '_' || c == '-';
		case _PathParserState.TagName:
			return Char.IsAlphaNumeric(c) || c == '_' || c == '-' || c == '*';
	}
	return false;
}

_FindPathStep.prototype.HasRules = function HasRules()
{
	return this._ids.length > 0
		|| this._classNames.length > 0
		|| this._tagNames.length > 0
		|| this._attributes.length > 0;
}

_FindPathStep.prototype._Parse = function _Parse(path)
{
	var state = _PathParserState.Expect;
	var sb = new StringBuilder();
	var good = true, tagsDone = false, idsDone = false, classNamesDone = false, pseudoClassesDone = false, joinDone = false;
	var attrName = null, attrOper = null;
	for (; path._index <= path._path.length; ++path._index)
	{
		var c = path._GetChar();

		if (_FindPathStep._IsMatch(c, state))
		{
			sb.Append(c); // append - whatever it is
		}
		else
		{
			var identifier = sb.toString();
			sb.Clear();

			switch (state)
			{
				case _PathParserState.Expect:
					switch (c)
					{
						case '>': // child
						case '+': // immediate previous sibling
						case '~': // previous sibling
							if (this.HasRules())
							{
								good = false;
							}
							else
							{
								if (joinDone)
									FindPath.ThrowError("step join already processed");
								joinDone = true;

								switch (c)
								{
									case '>':
										this._join = _PathElementJoin.Child;
										break;
									case '+':
										this._join = _PathElementJoin.ImmediatePreviousSibling;
										break;
									case '~':
										this._join = _PathElementJoin.PreviousSibling;
										break;
								}
							}
							break;
						case '.':
							if (classNamesDone)
								FindPath.ThrowError("class names already processed");

							state = _PathParserState.ClassName;
							this._classNames.push(new Array());
							break;
						case '#':
							if (idsDone)
								FindPath.ThrowError("IDs already processed");

							state = _PathParserState.Ids;
							break;
						case '[':
							++path._index;
							var attr = new _AttributeComparison();
							attr._Parse(path);
							this._attributes.push(attr);
							break;
						case ':':
							++path._index;
							var pseudoClass = new _PseudoClassComparison();
							pseudoClass._Parse(path);
							this._pseudoClasses.push(pseudoClass);
							break;
						case ',': // end of option
							good = false;
							break;
						default:
							if (_FindPathStep._IsMatch(c, _PathParserState.TagName))
							{
								if (tagsDone)
									FindPath.ThrowError("tags already processed");

								path._GoBack();
								state = _PathParserState.TagName;
							}
							else if (Char.IsWhiteSpace(c))
							{ // noop
							}
							else
								FindPath.InvalidCharacterError(c);
					}
					break;
				case _PathParserState.TagName:
					if (identifier != "")
					{
						tagsDone = true;

						if (identifier == "*")
							this._tagNames.splice(0, this._tagNames.length, "*");
						else
							this._tagNames.push(identifier.toUpperCase());

						switch (c)
						{
							case '|': // another tag name in the OR list
								break;
							default:
								state = _PathParserState.Expect;
								path._GoBack();
								break;
						}
					}
					break;
				case _PathParserState.Ids:
					if (identifier == "")
						FindPath.ThrowError("ID can't be empty");

					this._ids.push(identifier);
					tagsDone = idsDone = true;

					switch (c)
					{
						case '|':
							break;
						default:
							state = _PathParserState.Expect;
							path._GoBack();
							break;
					}
					break;
				case _PathParserState.ClassName:
					this._classNames[this._classNames.length - 1].push(identifier);
					tagsDone = classNamesDone = true;
					switch (c)
					{
						case '.': // another group of class names in AND list
							this._classNames.push(new Array());
							break;
						case '|': // another class name in OR list
							break;
						default:
							state = _PathParserState.Expect;
							path._GoBack();
							break;
					}
					break;
			}
		}
		if (!good)
			break;
	}
	path._GoBack();
	if (this.HasRules() && this._tagNames.length == 0)
		this._tagNames.push("*");
	return this.HasRules();
}
_FindPathStep.prototype._CheckTags = function _CheckTags(elem)
{
	if (this._tagNames[0] == "*") return true;

	var tagsMatch = false;
	for (var tId = 0; tId < this._tagNames.length; ++tId)
	{
		var tagName = this._tagNames[tId];
		if (elem.tagName == tagName)
		{
			tagsMatch = true;
			break;
		}
	}
	return tagsMatch;
}
_FindPathStep.prototype._CheckClasses = function _CheckClasses(elem)
{
	for (var iClassGroup = 0; iClassGroup < this._classNames.length; ++iClassGroup)
	{
		var classGroup = this._classNames[iClassGroup];

		var classesMatch = false;
		for (var iClass = 0; iClass < classGroup.length; ++iClass)
		{
			var className = classGroup[iClass];
			if(elem.HasClass(className))
			{
				classesMatch = true;
				break;
			}
		}

		if (!classesMatch)
			return false;
	}
	return true;
}
_FindPathStep.prototype._CheckIds = function _CheckIds(elem)
{
	if (this._ids.length > 0)
	{
		var idsMatch = false;
		for (var iId = 0; iId < this._ids.length; ++iId)
		{
			var id = this._ids[iId];
			if (elem.id == id)
				return true; // got it
		}
		return false; // no match
	}
	return true; // no ids - everything is a match
}
_FindPathStep.prototype._CheckAttributes = function _CheckAttributes(elem)
{
	var attributesMatch = true;
	for (var i = 0; i < this._attributes.length; ++i)
	{
		var attr = this._attributes[i];
		if (____a) attr = new _AttributeComparison();

		if (!attr._Check(elem))
			return false;
	}
	return true;
}
_FindPathStep.prototype._CheckPseudoClasses = function _CheckPseudoClasses(elem)
{
	for (var i = 0; i < this._pseudoClasses.length; ++i)
	{
		var pseudoClass = this._pseudoClasses[i];
		if (____a) pseudoClass = new _PseudoClassComparison();

		if (!pseudoClass._Check(elem))
			return false;
	}
	return true;
}
_FindPathStep.prototype._Check = function _Check(elem, ignoreTags, ignoreClassNames)
{
	if (ignoreTags === undefined)
		ignoreTags = false;
	if (ignoreClassNames === undefined)
		ignoreClassNames = false;

	if (!ignoreTags)
	{
		if (!this._CheckTags(elem))
			return false;
	}

	if (!ignoreClassNames)
	{
		if (!this._CheckClasses(elem))
			return false;
	}

	if (!this._CheckIds(elem))
		return false;

	if (!this._CheckAttributes(elem))
		return false;

	if (!this._CheckPseudoClasses(elem))
		return false;

	return true;
}
_FindPathStep.prototype._FillClassQueries = function _FillClassQueries(iClassAnd, prev)
{
	var res = new Array();
	if (iClassAnd >= this._classNames.length)
	{
		if (prev != null)
			res.push(prev);
		return res;
	}

	if (prev != null)
		prev += " ";
	else
		prev = "";

	var curGroup = this._classNames[iClassAnd];
	for (var iClassOr = 0; iClassOr < curGroup.length; ++iClassOr)
	{
		var className = curGroup[iClassOr];

		var t = prev + className;
		var next = this._FillClassQueries(iClassAnd + 1, t);
		res.AddRange(next);
	}
	return res;
}
_FindPathStep.prototype._Find = function _Find(startingPoint, children)
{
	var res = new Array();
	if (children)
	{
		var cn = startingPoint.get_ChildNodes(), cnl = cn.length;
		for (var iChild = 0; iChild < cnl; ++iChild)
		{
			var child = cn[iChild];
			if (child.nodeName == "#text") continue;

			if (this._Check(child, false, false))
				res.push(child);
		}
	}
	else
	{
		var useTagSearch = true;
		if (document.getElementsByClassName !== undefined)
		{
			var classQueries = this._FillClassQueries(0, null);

			var numTags = this._tagNames.length == 1 && this._tagNames[0] == "*" ? 0 : this._tagNames.length,
				numClasses = classQueries.length;

			if (numClasses >= numTags)
			{
				useTagSearch = false;

				var classNames = this._classNames[0];
				for (var iClass = 0; iClass < classQueries.length; ++iClass)
				{
					var c = classQueries[iClass];
					var t = startingPoint.getElementsByClassName(c);
					for (var iElem = 0; iElem < t.length; ++iElem)
					{
						if (this._Check(t[iElem], false, true))
							res.push(t[iElem]);
					}
				}
			}
		}

		if (useTagSearch)
		{
			for (var iTag = 0; iTag < this._tagNames.length; ++iTag)
			{
				var tagName = this._tagNames[iTag];
				var t = startingPoint.getElementsByTagName(tagName);
				
				
				for (var iElem = 0; iElem < t.length; ++iElem)
				{
					if (this._Check(t[iElem], true, false))
						res.push(t[iElem]);
				}
			}
		}
	}
	return res;
}



















































function _FindPathOption()
{
	this._steps = new Array();
}
_FindPathOption.prototype._steps = new Array();
_FindPathOption.prototype._Parse = function _Parse(path)
{
	for (; path._index < path._path.length; ++path._index)
	{
		var element = new _FindPathStep();
		if (element._Parse(path))
		{
			this._steps.push(element);
		}
		else
			break;
	}
	return this._steps.length > 0;
}
_FindPathOption.prototype._Find = function _Find(startingPoint, children, first)
{
	var res = this._steps[this._steps.length - 1]._Find(startingPoint, children);
	for (var iElem = 0; iElem < res.length; ++iElem)
	{
		var elems = new Array();
		elems.push(res[iElem]);

		for (var iStep = this._steps.length - 2; elems.length > 0 && iStep >= 0; --iStep)
		{
			var newList = new Array();
			var prevStep = this._steps[iStep + 1], nextStep = this._steps[iStep];
			if (____a)
			{
				prevStep = nextStep = new _FindPathStep();
			}

			switch (prevStep._join)
			{
				case _PathElementJoin.Descendant:
					if (children)
						FindPath.ThrowError("can't have Descendant relationship in a Childrens request");

					for (var iTemp = 0; iTemp < elems.length; ++iTemp)
					{
						var elem = elems[iTemp];

						while (elem.parentNode != startingPoint)
						{
							elem = elem.parentNode;
							if (nextStep._Check(elem))
							{
								if (!newList.Contains(elem))
									newList.push(elem);
							}
						}
					}
					elems = newList;
					break;
				case _PathElementJoin.Child:
					if (children)
						FindPath.ThrowError("can't have Child relationship in a Childrens request");
					for (var iTemp = 0; iTemp < elems.length; ++iTemp)
					{
						var elem = elems[iTemp];

						elem = elem.parentNode;
						if (elem != startingPoint && nextStep._Check(elem))
						{
							if (!newList.Contains(elem))
								newList.push(elem);
						}
					}
					break;
				case _PathElementJoin.ImmediatePreviousSibling:
					for (var iTemp = 0; iTemp < elems.length; ++iTemp)
					{
						var elem = elems[iTemp];
						do
						{
							elem = elem.previousSibling;
						}
						while (elem != null && elem.nodeName == "#text");
						if (elem != null && nextStep._Check(elem))
						{
							if (!newList.Contains(elem))
								newList.push(elem);
						}
					}
					break;
				case _PathElementJoin.PreviousSibling:
					for (var iTemp = 0; iTemp < elems.length; ++iTemp)
					{
						var elem = elems[iTemp];
						while (elem.previousSibling != null)
						{
							elem = elem.previousSibling;
							if (elem.nodeName == "#text") continue;

							if (nextStep._Check(elem))
							{
								if (!newList.Contains(elem))
									newList.push(elem);
							}
						}
					}
					break;
			}
			elems = newList;
		}

		if (elems.length == 0)
		{
			res.splice(iElem, 1);
			--iElem;
		}
		else
		{
			if (first)
				return res[iElem];
		}
	}
	if (first)
		return null;
	return res;
}
_FindPathOption.prototype._Children = function _Children(startingPoint)
{
	var res = this._steps[this._steps.length - 1]._Children(startingPoint);
	for (var iElem = 0; iElem < res.length; ++iElem)
	{
		var elems = new Array();
		elems.push(res[iElem]);

		for (var iStep = this._steps.length - 2; elems.length > 0 && iStep >= 0; --iStep)
		{
			var newList = new Array();
			var prevStep = this._steps[iStep + 1], nextStep = this._steps[iStep];
			if (____a)
			{
				prevStep = nextStep = new _FindPathStep();
			}

			switch (prevStep._join)
			{
				case _PathElementJoin.Descendant:
					FindPath.ThrowError("can't have Descendant relationship in a Childrens request");
				case _PathElementJoin.Child:
					FindPath.ThrowError("can't have Child relationship in a Childrens request");
				case _PathElementJoin.ImmediatePreviousSibling:
					for (var iTemp = 0; iTemp < elems.length; ++iTemp)
					{
						var elem = elems[iTemp];
						do
						{
							elem = elem.previousSibling;
						}
						while (elem != null && elem.nodeName == "#text");
						if (elem != null && nextStep._Check(elem))
						{
							if (!newList.Contains(elem))
								newList.push(elem);
						}
					}
					break;
			}
			elems = newList;
		}

		if (elems.length == 0)
		{
			res.splice(iElem, 1);
			--iElem;
		}
	}
	return res;
}

























function FindPath()
{

}

FindPath.prototype._index = 0;
FindPath.prototype._path = "";
FindPath.prototype._options = new Array();

FindPath.ThrowError = function ThrowError(msg)
{
	Throw("Incorrect syntax: " + msg + ".");
}
FindPath.InvalidCharacterError = function InvalidCharacterError(c)
{
	FindPath.ThrowError("found invalid character '" + c + "'");
}
FindPath.ExpectedButFoundError = function ExpectedButFoundError(cFound, cExpected)
{
	FindPath.ThrowError("expected '" + cExpected + "' but found '" + cFound + "'");
}

FindPath.prototype.Parse = function Parse(path)
{
	if (____a) path = "";
	this._path = path;
	this._index = 0;
	this._options = new Array();
	var commaDone = false;
	for (; this._index < this._path.length; ++this._index)
	{
		var c = this._GetChar();
		if (c == ',')
		{
			if (commaDone)
				FindPath.ThrowError("multiple commas");
			commaDone = true;
		}
		else if (Char.IsWhiteSpace(c))
		{ // nothing
		}
		else
		{
			var option = new _FindPathOption();
			if (option._Parse(this))
			{
				this._options.push(option);
				commaDone = false;
			}
			else
				break;
		}
	}
}

FindPath.prototype._GetChar = function _GetChar()
{
	return this._index == this._path.length ? ' ' : this._path.charAt(this._index);
}
FindPath.prototype._GetNextChar = function _GetNextChar()
{
	++this._index;
	return this._index == this._path.length ? ' ' : this._path.charAt(this._index);
}
FindPath.prototype._VerifyNextChar = function _VerifyNextChar(c)
{
	var t;
	if ((t = this._GetNextChar()) != c)
		FindPath.ExpectedButFoundError(c, t);
}
FindPath.prototype._SkipWhiteSpaces = function _SkipWhiteSpaces()
{
	var c;
	while (this._index < this._path.length && Char.IsWhiteSpace(c = this._GetChar()))
	{
		++this._index;
	}
	return c;
}
FindPath.prototype._GoBack = function _GoBack()
{
	--this._index;
}
FindPath.prototype._Find = function _Find(elem, children, first)
{
	if (!first)
		var res = new Array();
	for (var iOption = 0; iOption < this._options.length; ++iOption)
	{
		var t = this._options[iOption]._Find(elem, children, first);
		if (first)
		{
			if (t != null)
				return t;
		}
		else
		{
			for (var i = 0; i < t.length; ++i)
				res.push(t[i]);
		}
	}
	return res;
}






















document.Find = Element.prototype.Find = function Find(path)
{
	var res;
	//	if (this.querySelectorAll && !path.Contains("|"))
	//	{
	//		res = this.querySelectorAll(path);
	//	}
	//	else
	{
		var f = new FindPath();
		f.Parse(path);
		res = f._Find(this, false, false);
	}
	if (Element.applyAllPropertiesArray != null)
		Element.applyAllPropertiesArray(res);

	return res;
}
document.FindFirst = Element.prototype.FindFirst = function FindFirst(path)
{
	var res;
	//	if (this.querySelector != null && !path.Contains("|"))
	//	{
	//		res = this.querySelector(path);
	//	}
	//	else
	{
		var f = new FindPath();
		f.Parse(path);
		res = f._Find(this, false, true);
	}

	if (Element.applyAllProperties != null)
		Element.applyAllProperties(res);

	return res;
}



Element.prototype.FindChildren = function FindChildren(path)
{
	var f = new FindPath();
	f.Parse(path);
	var res = f._Find(this, true, false);

	if (Element.applyAllPropertiesArray != null)
		Element.applyAllPropertiesArray(res);

	return res;
}
Element.prototype.FindFirstChild = function FindFirstChild(path)
{
	var f = new FindPath();
	f.Parse(path);
	var res = f._Find(this, true, true);

	if (Element.applyAllProperties != null)
		Element.applyAllProperties(res);

	return res;
}
